/*=============================================================================
  ShadowUtils.cpp : 
  Copyright (c) 2001-2005 Crytek Studios. All Rights Reserved.

  Revision history:
    * Created by Nick Kasyan
=============================================================================*/
#include "StdAfx.h"
#include "ShadowUtils.h"

bool CPoissonDiskGen::m_bCached = false;
int CPoissonDiskGen::m_numSamples = 0;
Vec2* CPoissonDiskGen::m_pvSamples = NULL;

bool CShadowUtils::bShadowFrustumCacheValid = false;

void CShadowUtils::CalcDifferentials(const CCamera& cam, float fViewWidth, float fViewHeight, float& fFragSizeX)
{
	//
	Vec3 vNearEdge = cam.GetEdgeN();

	float fFar = cam.GetFarPlane();
	float fNear = abs(vNearEdge.y);
	float fWorldWidthDiv2 = abs(vNearEdge.x);
	float fWorldHeightDiv2 = abs(vNearEdge.z);

	fFragSizeX = (fWorldWidthDiv2 * 2.0f) / fViewWidth;
	float fFragSizeY = (fWorldHeightDiv2 * 2.0f) / fViewHeight;

	float fDDZ = fWorldWidthDiv2 / fViewHeight;

	return;
}


void CShadowUtils::CalcScreenToWorldExpansionBasis(const CCamera& cam, float fViewWidth, float fViewHeight, Vec3& vWBasisX, Vec3& vWBasisY, Vec3& vWBasisZ, bool bWPos)
{

	const Matrix34& camMatrix = cam.GetMatrix();

	Vec3 vNearEdge = cam.GetEdgeN();

	//all values are in camera space
	float fFar = cam.GetFarPlane();
	float fNear = abs(vNearEdge.y);
	float fWorldWidthDiv2 = abs(vNearEdge.x);
	float fWorldHeightDiv2 = abs(vNearEdge.z);

	float k = fFar/fNear;

	Vec3 vStereoShift = camMatrix.GetColumn0().GetNormalized() * cam.GetAsymL();

	Vec3 vZ = (camMatrix.GetColumn1().GetNormalized() * fNear + vStereoShift) * k; // size of vZ is the distance from camera pos to near plane
	Vec3 vX = camMatrix.GetColumn0().GetNormalized() * (fWorldWidthDiv2 * k);
	Vec3 vY = camMatrix.GetColumn2().GetNormalized() * (fWorldHeightDiv2 * k);

	//Add view position to constant vector to avoid additional ADD in the shader
	//vZ = vZ + cam.GetPosition();

	//WPos basis adjustments
	if (bWPos)
	{
		//float fViewWidth = cam.GetViewSurfaceX();
		//float fViewHeight = cam.GetViewSurfaceZ();

		vZ = vZ - vX;
		vX *= (2.0f/fViewWidth);
		//vX *= 2.0f; //uncomment for ScreenTC

		vZ = vZ + vY;
		vY *= -(2.0f/fViewHeight);
		//vY *= -2.0f; //uncomment for ScreenTC
	}

	vWBasisX = vX;
	vWBasisY = vY;
	vWBasisZ = vZ;

}


void CShadowUtils::ProjectScreenToWorldExpansionBasis(const Matrix44r& mShadowTexGen, const CCamera& cam, float fViewWidth, float fViewHeight, Vec4r& vWBasisX, Vec4r& vWBasisY, Vec4r& vWBasisZ, Vec4r& vCamPos, bool bWPos, SRenderTileInfo* pSTileInfo)
{

	const Matrix34& camMatrix = cam.GetMatrix();

	Vec3 vNearEdge = cam.GetEdgeN();

	//all values are in camera space
	float fFar = cam.GetFarPlane();
	float fNear = abs(vNearEdge.y);
	float fWorldWidthDiv2 = abs(vNearEdge.x);
	float fWorldHeightDiv2 = abs(vNearEdge.z);

	float k = fFar/fNear;

	//simple non-general hack to shift stereo with off center projection
	Vec3 vStereoShift = camMatrix.GetColumn0().GetNormalized() * cam.GetAsymL();
	Vec3 vZ = (camMatrix.GetColumn1().GetNormalized() * fNear + vStereoShift)* k; // size of vZ is the distance from camera pos to near plane

	Vec3 vX = camMatrix.GetColumn0().GetNormalized() * fWorldWidthDiv2 * k;
	Vec3 vY = camMatrix.GetColumn2().GetNormalized() * fWorldHeightDiv2 * k;

	//Add view position to constant vector to avoid additional ADD in the shader
	//vZ = vZ + cam.GetPosition();

	//multi-tiled render handling
	if (pSTileInfo)
	{
		vZ = vZ + ( vX*(2.0f * (pSTileInfo->nGridSizeX-1.0f-pSTileInfo->nPosX)/pSTileInfo->nGridSizeX) );
		vZ = vZ - ( vY*(2.0f * (pSTileInfo->nGridSizeY-1.0f-pSTileInfo->nPosY)/pSTileInfo->nGridSizeY) );
	}

	//TFIX calc how k-scale does work with this projection
	//WPos basis adjustments
	if (bWPos)
	{

		//float fViewWidth = cam.GetViewSurfaceX();
		//float fViewHeight = cam.GetViewSurfaceZ();
		vZ = vZ - vX;
		vX *= (2.0f/fViewWidth);
		//vX *= 2.0f; //uncomment for ScreenTC

		vZ = vZ + vY;
		vY *= -(2.0f/fViewHeight);
		//vY *= -2.0f; //uncomment for ScreenTC
	}

	//multi-tiled render handling
	if (pSTileInfo)
	{
		vX *= 1.0f/pSTileInfo->nGridSizeX;
		vY *= 1.0f/pSTileInfo->nGridSizeY;
	}

	//TOFIX: create PB components for these params
	//creating common projection matrix for depth reconstruction
	vWBasisX = mShadowTexGen * Vec4r(vX, 0.0f);
	vWBasisY = mShadowTexGen * Vec4r(vY, 0.0f);
	vWBasisZ = mShadowTexGen * Vec4r(vZ, 0.0f);
	vCamPos =  mShadowTexGen * Vec4r(cam.GetPosition(), 1.0f);

}

void CShadowUtils::CalcLightBoundRect(const SRenderLight* pLight, const CRenderCamera& RCam, Matrix44A& mView, Matrix44A& mProj, Vec2* pvMin,  Vec2* pvMax, IRenderAuxGeom* pAuxRend)
{
  Vec3 vViewVec = Vec3(pLight->m_Origin - RCam.Orig);
  float fDistToLS =  vViewVec.GetLength();

  if (fDistToLS<=pLight->m_fRadius)
  {
    //optimization when we are inside light frustum
    *pvMin = Vec2(0,0);
    *pvMax = Vec2(1,1);
    return;
  }


  float fRadiusSquared = pLight->m_fRadius * pLight->m_fRadius;

  //distance from Light Source center to the bound plane
  float fDistToBoundPlane = fRadiusSquared/fDistToLS;

  float fQuadEdge = sqrtf(fRadiusSquared - fDistToBoundPlane * fDistToBoundPlane);

  vViewVec.SetLength( fDistToLS - fDistToBoundPlane );

  Vec3 vCenter = RCam.Orig +  vViewVec;

  Vec3 vUp = vViewVec.Cross(RCam.Y.Cross(vViewVec));
  Vec3 vRight = vViewVec.Cross(RCam.X.Cross(vViewVec));

  vUp.normalize();
  vRight.normalize();

  float fRadius = pLight->m_fRadius;

  Vec3 pBRectVertices[4] =
  {
    vCenter + (vUp*fQuadEdge) -  (vRight*fQuadEdge),
    vCenter + (vUp*fQuadEdge) +  (vRight*fQuadEdge),
    vCenter - (vUp*fQuadEdge) +  (vRight*fQuadEdge),
    vCenter - (vUp*fQuadEdge) -  (vRight*fQuadEdge)
  };

  Vec3 pBBoxVertices[8] =
  {
    Vec3(Vec3(vCenter.x, vCenter.y, vCenter.z) + Vec3(-fRadius,-fRadius, fRadius)),
    Vec3(Vec3(vCenter.x, vCenter.y, vCenter.z) + Vec3(-fRadius, fRadius, fRadius)),
    Vec3(Vec3(vCenter.x, vCenter.y, vCenter.z) + Vec3( fRadius,-fRadius, fRadius)),
    Vec3(Vec3(vCenter.x, vCenter.y, vCenter.z) + Vec3( fRadius, fRadius, fRadius)),

    Vec3(Vec3(vCenter.x, vCenter.y, vCenter.z) + Vec3(-fRadius,-fRadius, -fRadius)),
    Vec3(Vec3(vCenter.x, vCenter.y, vCenter.z) + Vec3(-fRadius, fRadius, -fRadius)),
    Vec3(Vec3(vCenter.x, vCenter.y, vCenter.z) + Vec3( fRadius,-fRadius, -fRadius)),
    Vec3(Vec3(vCenter.x, vCenter.y, vCenter.z) + Vec3( fRadius, fRadius, -fRadius))
  };

  //TODO: cache inverted mitrices for all deff meshes reconstructions
  Matrix44A mInvView = mView.GetInverted();

  *pvMin = Vec2(1,1);
  *pvMax = Vec2(0,0);

  for (int i = 0; i<4; i++)
  {
    if (pAuxRend!=NULL)
    {
      pAuxRend->DrawPoint( pBRectVertices[i] ,RGBA8(0xff,0xff,0xff,0xff),10);

      int32 nPrevVert = (i-1)<0?3:(i-1);

      pAuxRend->DrawLine( pBRectVertices[nPrevVert], RGBA8(0xff,0xff,0x0,0xff), pBRectVertices[i], RGBA8(0xff,0xff,0x0,0xff), 3.0f);
    }


    Vec4 vScreenPoint = Vec4(pBRectVertices[i], 1.0) * mProj;

    //projection space clamping
    vScreenPoint.w = max(vScreenPoint.w, 0.00000000000001f);

    vScreenPoint.x = max(vScreenPoint.x, -(vScreenPoint.w));
    vScreenPoint.x = min(vScreenPoint.x, vScreenPoint.w);
    vScreenPoint.y = max(vScreenPoint.y, -(vScreenPoint.w));
    vScreenPoint.y = min(vScreenPoint.y, vScreenPoint.w);


    //NDC
    vScreenPoint /= vScreenPoint.w;

    //output coords
    //generate viewport (x=0,y=0,height=1,width=1)
    Vec2 vWin;
    vWin.x = (1 + vScreenPoint.x)/ 2;
    vWin.y = (1 + vScreenPoint.y)/ 2;  //flip coords for y axis

    assert(vWin.x>=0.0f && vWin.x<=1.0f);
    assert(vWin.y>=0.0f && vWin.y<=1.0f);

    pvMin->x = min( pvMin->x,vWin.x );
    pvMin->y = min( pvMin->y,vWin.y );
    pvMax->x = max( pvMax->x,vWin.x );
    pvMax->y = max( pvMax->y,vWin.y );
  }
}

void CShadowUtils::GetProjectiveTexGen(const SRenderLight* pLight, int nFace, Matrix44A* mTexGen)
{
  assert(pLight!=NULL);
  float fOffsetX = 0.5f;
  float fOffsetY = 0.5f;
#if defined (DIRECT3D9) || defined(PS3)
  ITexture *pLightTexture = pLight->GetLightTexture();
  fOffsetX += (0.5f / max( (float)pLightTexture->GetWidth(), 1.00001f) );
  fOffsetY += (0.5f / max( (float)pLightTexture->GetHeight(),1.00001f) );
#endif

  Matrix44A mTexScaleBiasMat = Matrix44A( 0.5f,     0.0f,     0.0f,    0.0f,  
                                         0.0f,    -0.5f,     0.0f,    0.0f,
                                         0.0f,     0.0f,     1.0f,    0.0f,
                                         fOffsetX, fOffsetY, 0.0f,    1.0f );

  Matrix44A mLightProj, mLightView;
  CShadowUtils::GetCubemapFrustumForLight(pLight, nFace, pLight->m_fLightFrustumAngle*2.f, &mLightProj, &mLightView, true);

  Matrix44 mLightViewProj = mLightView * mLightProj; 

  *mTexGen = mLightViewProj * mTexScaleBiasMat;
}

//TF: make support for cubemap faces
void CShadowUtils::GetCubemapFrustumForLight( const SRenderLight* pLight, int nS, float fFov, Matrix44A* pmProj, Matrix44A* pmView, bool bProjLight )
{
  Vec3 zaxis, yaxis, xaxis;

  //new cubemap calculation
  float sCubeVector[6][7] = 
  {
    {1,0,0,  0,0,-1, -90}, //posx
    {-1,0,0, 0,0,1,  90}, //negx
    {0,1,0,  -1,0,0, 0},  //posy
    {0,-1,0, 1,0,0,  0},  //negy
    {0,0,1,  0,-1,0,  0},  //posz
    {0,0,-1, 0,1,0,  0},  //negz 
  };

  /*const float sCubeVector[6][7] = 
  {
    {1,0,0,  0,0,1, -90}, //posx
    {-1,0,0, 0,0,1,  90}, //negx
    {0,1,0,  0,0,-1, 0},  //posy
    {0,-1,0, 0,0,1,  0},  //negy
    {0,0,1,  0,1,0,  0},  //posz
    {0,0,-1, 0,1,0,  0},  //negz 
  };*/

  Vec3 vForward = Vec3(sCubeVector[nS][0], sCubeVector[nS][1], sCubeVector[nS][2]);
  Vec3 vUp      = Vec3(sCubeVector[nS][3], sCubeVector[nS][4], sCubeVector[nS][5]);
  Vec3 vEyePt = pLight->m_Origin;
  vForward = vForward + vEyePt;

  //mathMatrixLookAt( pmView, vEyePt, vForward, vUp );

  //adjust light rotation
  Matrix34 mLightRot = pLight->m_ObjMatrix;

  //coord systems conversion(from orientation to shader matrix)
  if (bProjLight) //legacy orientation
  {
    zaxis = mLightRot.GetColumn1().GetNormalized();
    yaxis =-mLightRot.GetColumn0().GetNormalized();
    xaxis =-mLightRot.GetColumn2().GetNormalized();

  }
  else
  {
    zaxis = mLightRot.GetColumn2().GetNormalized();
    yaxis = -mLightRot.GetColumn0().GetNormalized();
    xaxis = mLightRot.GetColumn1().GetNormalized();
  }

  //Vec3 vLightDir = xaxis;

  //RH
  (*pmView)(0,0) = xaxis.x;		(*pmView)(0,1) = zaxis.x;	(*pmView)(0,2) = yaxis.x;	(*pmView)(0,3) = 0; 
  (*pmView)(1,0) = xaxis.y;		(*pmView)(1,1) = zaxis.y;	(*pmView)(1,2) = yaxis.y;	(*pmView)(1,3) = 0; 
  (*pmView)(2,0) = xaxis.z;		(*pmView)(2,1) = zaxis.z;	(*pmView)(2,2) = yaxis.z;	(*pmView)(2,3) = 0; 
  (*pmView)(3,0) = -xaxis.Dot(vEyePt);	(*pmView)(3,1) = -zaxis.Dot(vEyePt);	(*pmView)(3,2) = -yaxis.Dot(vEyePt);	(*pmView)(3,3) = 1; 

	float zn = max(pLight->m_fProjectorNearPlane, 0.01f);
	float zf = max(pLight->m_fRadius, zn + 0.01f);
  mathMatrixPerspectiveFov( pmProj, (float)DEG2RAD_R(fFov), 1.f, zn, zf);

  /*if (bShadowGen)
  {
    mathMatrixPerspectiveFov( pmProj, DEG2RAD_R(fFov), 1.f, max(pLight->m_fProjectorNearPlane,0.01f), pLight->m_fRadius);
  }
  else
  {
    mathMatrixPerspectiveFov( pmProj, DEG2RAD_R(fFov*1.001), 1.f, max(pLight->m_fProjectorNearPlane,0.01f)+0.01f, pLight->m_fRadius+3.0f);
  }*/
}



void CShadowUtils::GetCubemapFrustum(EFrustum_Type eFrustumType, ShadowMapFrustum* pFrust, int nS, Matrix44A* pmProj, Matrix44A* pmView, Matrix33* pmLightRot)
{
  //new cubemap calculation

  float sCubeVector[6][7] = 
  {
    {1,0,0,  0,0,-1, -90}, //posx
    {-1,0,0, 0,0,1,  90}, //negx
    {0,1,0,  -1,0,0, 0},  //posy
    {0,-1,0, 1,0,0,  0},  //negy
    {0,0,1,  0,-1,0,  0},  //posz
    {0,0,-1, 0,1,0,  0},  //negz 
  };

  /*const float sCubeVector[6][7] = 
  {
    {1,0,0,  0,0,1, -90}, //posx
    {-1,0,0, 0,0,1,  90}, //negx
    {0,1,0,  0,0,-1, 0},  //posy
    {0,-1,0, 0,0,1,  0},  //negy
    {0,0,1,  0,1,0,  0},  //posz
    {0,0,-1, 0,1,0,  0},  //negz 
  };*/

  Vec3 vForward = Vec3(sCubeVector[nS][0], sCubeVector[nS][1], sCubeVector[nS][2]);
  Vec3 vUp      = Vec3(sCubeVector[nS][3], sCubeVector[nS][4], sCubeVector[nS][5]);
  float fMinDist = max(pFrust->fNearDist, 0.03f);
  float fMaxDist = pFrust->fFarDist;

  Vec3 vEyePt = Vec3(
    pFrust->vLightSrcRelPos.x + pFrust->vProjTranslation.x,
    pFrust->vLightSrcRelPos.y + pFrust->vProjTranslation.y,
    pFrust->vLightSrcRelPos.z + pFrust->vProjTranslation.z
    );
  Vec3 At = vEyePt;

  if (eFrustumType==FTYP_OMNILIGHTVOLUME)
  {
    vEyePt -= (2.0f * fMinDist) * vForward.GetNormalized();
  }

  vForward = vForward + At;

  //mathMatrixTranslation(&mLightTranslate, -vPos.x, -vPos.y, -vPos.z);2
  mathMatrixLookAt( pmView, vEyePt, vForward, vUp );

  //adjust light rotation
  if (pmLightRot)
  {
    (*pmView) = (*pmView) * (*pmLightRot);
  }
	if (eFrustumType==FTYP_SHADOWOMNIPROJECTION)
	{
		//near plane does have big influence on the precision (depth distribution) for non linear frustums
		mathMatrixPerspectiveFov( pmProj, (float)DEG2RAD_R(g_fOmniShadowFov), 1.f, fMinDist, fMaxDist );
	}
	else
	if (eFrustumType==FTYP_OMNILIGHTVOLUME)
	{
		//near plane should be extremely small in order to avoid  seams on between frustums
		mathMatrixPerspectiveFov( pmProj, (float)DEG2RAD_R(g_fOmniLightFov), 1.f, fMinDist, fMaxDist );
	}

}

static _inline Vec3 frac(Vec3 in)
{
  Vec3 out;
  out.x = in.x - (float)(int)in.x;
  out.y = in.y - (float)(int)in.y;
  out.z = in.z - (float)(int)in.z;

  return out;
}

float snap_frac2(float fVal, float fSnap)
{
	float fValSnapped = fSnap*int64(fVal/fSnap);
	return fValSnapped;
}

//Right Handed
void CShadowUtils::mathMatrixLookAtSnap(Matrix44A* pMatr, const Vec3& Eye, const Vec3& At, ShadowMapFrustum* pFrust)
{
	const Vec3 vZ(0.f, 0.f, 1.f);
	const Vec3 vY(0.f, 1.f, 0.f);

	Vec3 vUp;
	Vec3 vEye = Eye;
	Vec3 vAt = At;

	Vec3 vLightDir = (vEye - vAt);
	vLightDir.Normalize();

	if ( fabsf(vLightDir.Dot(vZ))>0.9995f )
		vUp = vY;
	else
		vUp = vZ;

	float fKatetSize = 1000000.0f  * cry_tanf(DEG2RAD(pFrust->fFOV)*0.5f);

	assert(pFrust->nTexSize>0);
	float fSnapXY = fKatetSize * 2.f / pFrust->nTexSize; //texture size should be valid already

	//TD - add ratios to the frustum
	fSnapXY *= 2.0f;

	float fZSnap = 192.0f*2.0f / 16777216.f/*1024.0f*/;
	//fZSnap *= 32.0f;

	Vec3 zaxis = vLightDir.GetNormalized();
	Vec3 xaxis = (vUp.Cross(zaxis)).GetNormalized();
	Vec3 yaxis =zaxis.Cross(xaxis);

	//vAt -= xaxis * snap_frac(vAt.Dot(xaxis), fSnapXY);
	//vAt -= yaxis  * snap_frac(vAt.Dot(yaxis), fSnapXY);

	(*pMatr)(0,0) = xaxis.x;    (*pMatr)(0,1) = yaxis.x;  (*pMatr)(0,2) = zaxis.x;  (*pMatr)(0,3) = 0;
	(*pMatr)(1,0) = xaxis.y;    (*pMatr)(1,1) = yaxis.y;  (*pMatr)(1,2) = zaxis.y;  (*pMatr)(1,3) = 0;
	(*pMatr)(2,0) = xaxis.z;    (*pMatr)(2,1) = yaxis.z;  (*pMatr)(2,2) = zaxis.z;  (*pMatr)(2,3) = 0;
	(*pMatr)(3,0) = -xaxis.Dot(vEye); (*pMatr)(3,1) = -yaxis.Dot(vEye); (*pMatr)(3,2) = -zaxis.Dot(vEye); (*pMatr)(3,3) = 1;

	float fTranslX = (*pMatr)(3,0);
	float fTranslY = (*pMatr)(3,1);
	float fTranslZ = (*pMatr)(3,2);

	(*pMatr)(3,0) = snap_frac2(fTranslX, fSnapXY);
	(*pMatr)(3,1) = snap_frac2(fTranslY, fSnapXY);
	//(*pMatr)(3,2) = snap_frac2(fTranslZ, fZSnap);

}

//todo move frustum computations to the 3d engine
void CShadowUtils::GetShadowMatrixOrtho(Matrix44A& mLightProj, Matrix44A& mLightView, const Matrix44A& mViewMatrix, ShadowMapFrustum* lof, bool bViewDependent)
{





	mathMatrixPerspectiveFov(&mLightProj, (float)DEG2RAD_R(max(lof->fFOV,0.0000001f)), max(lof->fProjRatio,0.0001f), lof->fNearDist, lof->fFarDist);


	//linearized depth
	//mLightProj.m22/= lof->fFarDist;
	//mLightProj.m32/= lof->fFarDist;

	const Vec3 zAxis(0.f, 0.f, 1.f);
	const Vec3 yAxis(0.f, 1.f, 0.f);
	Vec3 Up;
	Vec3 Eye = Vec3(
		lof->vLightSrcRelPos.x+lof->vProjTranslation.x,
		lof->vLightSrcRelPos.y+lof->vProjTranslation.y,
		lof->vLightSrcRelPos.z+lof->vProjTranslation.z);
	Vec3 At = Vec3(lof->vProjTranslation.x, lof->vProjTranslation.y, lof->vProjTranslation.z);

	Vec3 vLightDir = At - Eye;
	vLightDir.Normalize();

	if (bViewDependent)
	{
		//we should have LightDir vector transformed to the view space

		Eye = GetTransposed44(mViewMatrix).TransformPoint(Eye);
		At = GetTransposed44(mViewMatrix).TransformPoint(At);

		vLightDir = GetTransposed44(mViewMatrix).TransformVector(vLightDir);
		//vLightDir.Normalize();

	}

	//get look-at matrix
	if (CRenderer::CV_r_ShadowsGridAligned && lof->m_Flags & DLF_DIRECTIONAL)
	{
		mathMatrixLookAtSnap(&mLightView, Eye, At, lof);
	}
	else
	{
		if ( fabsf(vLightDir.Dot(zAxis))>0.9995f )
			Up = yAxis;
		else
			Up = zAxis;

		mathMatrixLookAt(&mLightView, Eye, At, Up);
	}

	//we should transform coords to the view space, so shadows are oriented according to camera always
	if (bViewDependent)
	{
		mLightView = mViewMatrix * mLightView;
	}
}



CShadowUtils::CShadowUtils()
{
}

CShadowUtils::~CShadowUtils()
{
}


CPoissonDiskGen::CPoissonDiskGen()
{

}
CPoissonDiskGen::~CPoissonDiskGen()
{

}

Vec2& CPoissonDiskGen::GetSample(int ind)
{
	assert(ind<m_numSamples && ind>=0);

	return
		m_pvSamples[ind];
}

void CPoissonDiskGen::SetKernelSize(int num)
{
	if (m_numSamples != num && num > 0)
	{
		m_numSamples = num;

		SAFE_DELETE_ARRAY(m_pvSamples);

		m_pvSamples = new Vec2[m_numSamples];

		InitSamples();
	}

	return;
}

void CPoissonDiskGen::FreeMemory()
{
	SAFE_DELETE_ARRAY(m_pvSamples);
	m_numSamples = 0;
}

void CPoissonDiskGen::RandomPoint(Vec2& p)
{
	//generate random point inside circle
	do {
			p.x = rnd()- 0.5f;
			p.y = rnd()- 0.5f;
	} while (p.x*p.x + p.y*p.y > 0.25f);

	return;
}

//samples distance-based sorting
struct SamplesDistaceSort
{
	bool operator()(const Vec2& samplA, const Vec2& samplB) const
	{
		float R2sampleA = samplA.x*samplA.x + samplA.y*samplA.y;
		float R2sampleB = samplB.x*samplB.x + samplB.y*samplB.y;

		return 
			(R2sampleA < R2sampleB);
	}
};

void CPoissonDiskGen::InitSamples()
{
	const int nQ = 1000;

	RandomPoint(m_pvSamples[0]);

	for (int i = 1; i < m_numSamples; i++)
	{
		float dmax = -1.0;

		for (int c = 0; c < i * nQ; c++)
		{
			Vec2 curSample;
			RandomPoint (curSample);

			float dc = 2.0;
			for (int j = 0; j < i; j++)
			{
				float dj =
					(m_pvSamples[j].x - curSample.x) * (m_pvSamples[j].x - curSample.x) +
					(m_pvSamples[j].y - curSample.y) * (m_pvSamples[j].y - curSample.y);
				if (dc > dj)
					dc = dj;
			}

			if (dc > dmax)
			{
				m_pvSamples[i] = curSample;
				dmax = dc;
			}
		}
	}

	for (int i = 0; i < m_numSamples; i++)
	{
		m_pvSamples[i] *=2.0f;
		//CryLogAlways("Sample %i: (%.6f, %.6f)\n", i, m_pvSamples[i].x, m_pvSamples[i].y);
	}

	//samples sorting



	std::stable_sort(m_pvSamples, m_pvSamples + m_numSamples , SamplesDistaceSort());


	return;
}



void CShadowUtils::GetIrregKernel(float sData[][4], int nSamplesNum)
{
#define PACKED_SAMPLES 1
	//samples for cubemaps
	/*const Vec4 irreg_kernel[8]=
	{
	Vec4(0.527837f, -0.085868f, 0.527837f, 0),
	Vec4(-0.040088f, 0.536087f, -0.040088f, 0),
	Vec4(-0.670445f, -0.179949f, -0.670445f, 0),
	Vec4(-0.419418f, -0.616039f, -0.419418f, 0),
	Vec4(0.440453f, -0.639399f, 0.440453f, 0),
	Vec4(-0.757088f, 0.349334f, -0.757088f, 0),
	Vec4(0.574619f, 0.685879f, 0.574619f, 0),
	Vec4(0.03851f, -0.939059f, 0.03851f, 0)
	};
	f32 fFrustumScale = r->m_cEF.m_TempVecs[4][0]; //take only first cubemap
	for (int i=0; i<8; i++)
	{
	sData[i].f[0] = irreg_kernel[i][0] * (1.0f/fFrustumScale);
	sData[i].f[1] = irreg_kernel[i][1] * (1.0f/fFrustumScale);
	sData[i].f[2] = irreg_kernel[i][2] * (1.0f/fFrustumScale);
	sData[i].f[3] = 0;
	}*/

	CPoissonDiskGen::SetKernelSize(nSamplesNum);

#ifdef PACKED_SAMPLES
	for (int i=0, nIdx=0; i<nSamplesNum; i+=2, nIdx++)
	{
		Vec2 vSample = CPoissonDiskGen::GetSample(i);
		sData[nIdx][0] = vSample.x;
		sData[nIdx][1] = vSample.y;
		vSample = CPoissonDiskGen::GetSample(i+1);
		sData[nIdx][2] = vSample.x;
		sData[nIdx][3] = vSample.y;
	}
#else
	for (int i=0, nIdx=0; i<nSamplesNum; i++, nIdx++)
	{
		Vec2 vSample = CPoissonDiskGen::GetSample(i);
		sData[nIdx][0] = vSample.x;
		sData[nIdx][1] = vSample.y;
		sData[nIdx][2] = 0.0f;
		sData[nIdx][3] = 0.0f;
	}
#endif

#undef PACKED_SAMPLES

}

ShadowMapFrustum* CShadowUtils::GetFrustum(ShadowFrustumID nFrustumID)
{
	int nLOD = 0;
	const int nLightID = GetShadowLightID(nLOD, nFrustumID);
	const int nThreadID = gRenDev->m_RP.m_nProcessThreadID;
	const int nCurRecLevel = SRendItem::m_RecurseLevel[nThreadID]-1;
# ifndef _RELEASE
	if (nCurRecLevel<0) __debugbreak();
# endif
	const int nDLights = gRenDev->m_RP.m_DLights[nThreadID][nCurRecLevel].size();

	const int nFrustumIdx = nLightID;
	assert((unsigned int) nFrustumIdx < (MAX_REND_LIGHTS+MAX_DEFERRED_LIGHTS));
	const int nStartIdx = SRendItem::m_StartFrust[nThreadID][nFrustumIdx];
	const int nEndIdx = SRendItem::m_EndFrust[nThreadID][nFrustumIdx];

	const int nSize = gRenDev->m_RP.m_SMFrustums[nThreadID][nCurRecLevel].size();
	for (int nFrIdx = nStartIdx; nFrIdx < nEndIdx && nFrIdx < nSize; ++nFrIdx)
	{
		ShadowMapFrustum* frustum = &gRenDev->m_RP.m_SMFrustums[nThreadID][nCurRecLevel][nFrIdx];
		if (frustum->nShadowMapLod == nLOD)
			return frustum;
	}

	assert(0);
	return NULL;
}

ShadowMapFrustum& CShadowUtils::GetFirstFrustum(int nLightID)
{
	const int nThreadID = gRenDev->m_RP.m_nProcessThreadID;
	const int nCurRecLevel = SRendItem::m_RecurseLevel[nThreadID]-1;
# ifndef _RELEASE
  if (nCurRecLevel<0) __debugbreak();
# endif
	const int nDLights = gRenDev->m_RP.m_DLights[nThreadID][nCurRecLevel].size();

	const int nFrustumIdx = nLightID + nDLights;
	assert((unsigned int) nFrustumIdx < (MAX_REND_LIGHTS+MAX_DEFERRED_LIGHTS));
	const int nStartIdx = SRendItem::m_StartFrust[nThreadID][nFrustumIdx];

	ShadowMapFrustum& firstFrustum = gRenDev->m_RP.m_SMFrustums[nThreadID][nCurRecLevel][nStartIdx];
	return firstFrustum;
}

const CShadowUtils::ShadowFrustumIDs * CShadowUtils::GetShadowFrustumList(uint64 nMask)
{
	// TODO: proper fix for per render object shadow light mask
	return NULL;

	if (!nMask)
		return NULL;

	if (!bShadowFrustumCacheValid)
	{
		// clear all allocated lists
		for(CRenderer::ShadowFrustumListsCache::iterator it = gRenDev->m_FrustumsCache.begin();
			it != gRenDev->m_FrustumsCache.end(); ++it)
			(it->second)->Clear();
		bShadowFrustumCacheValid = true;
	}

	CShadowUtils::ShadowFrustumIDs * pList = stl::find_in_map( gRenDev->m_FrustumsCache, nMask, NULL );
	if (!pList)
		pList = new CShadowUtils::ShadowFrustumIDs;
	if (pList->empty())
	{
		assert(nSunLightID <= 0xFF);
		// add sun index and lods encoded in higher bits
		for (int i = 0; i < 8; ++i)
		{
			if (nMask & (uint64(1) << i))
				pList->Add((i << 8) | (nSunLightID & 0xFF));
		}
		// other lights have 1 lod and index encoded in low bits
		for (int i = 8; i < 64 && (uint64(1)<<i) <= nMask; ++i)
		{
			if (nMask & (uint64(1) << i))
				pList->Add(i);
		}
		gRenDev->m_FrustumsCache[nMask] = pList;
	}
	return pList;
}

void ShadowMapFrustum::GetMemoryUsage(ICrySizer * pSizer) const
{
	pSizer->AddObject(this, sizeof(*this));
	pSizer->AddObject(pFrustumOwner);
	pSizer->AddObject(pDepthTex);
	pSizer->AddObject(pDepthTexArray);
}
